#include "imageinstancedata.h"

#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmimgle/dcmimage.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcuid.h"
#include "dcmtk/dcmjpeg/djdecode.h"

#include <QFile>

ImageInstanceData::ImageInstanceData(const QString &file):
    dcmff(0),
    dcmImage(0),
    pixelX(0),
    pixelY(0),
    imageFile(file)
{
    dcmff = new DcmFileFormat;
    if (dcmff->loadFile(file.toLocal8Bit().data()).good())
        initImage();
}

ImageInstanceData::ImageInstanceData(DcmFileFormat *dff):
    dcmff(dff),
    dcmImage(0),
    pixelX(0),
    pixelY(0)
{
    initImage();
}

ImageInstanceData::~ImageInstanceData()
{
    delete dcmImage;
    delete dcmff;
    DJDecoderRegistration::cleanup();
}

bool ImageInstanceData::isNormal() const
{
    return dcmImage && (dcmImage->getStatus() == EIS_Normal);
}

void ImageInstanceData::initImage()
{
    DJDecoderRegistration::registerCodecs();
    DcmDataset *dset;
    OFCondition result;
    if (dcmff && (dset = dcmff->getDataset())) {
        //dcmff->loadAllDataIntoMemory();
        dset->chooseRepresentation(EXS_LittleEndianExplicit, 0);

        const char *val = 0;
        result = dset->findAndGetString(DCM_StudyInstanceUID, val);
        studyUid = QString::fromLocal8Bit(val);
        result = dset->findAndGetString(DCM_SeriesInstanceUID, val);
        seriesUid = QString::fromLocal8Bit(val);
        result = dset->findAndGetString(DCM_SOPInstanceUID, val);
        imageUid = QString::fromLocal8Bit(val);
        result = dset->findAndGetString(DCM_SOPClassUID, val);
        classUid = QString::fromLocal8Bit(val);

        result = dset->findAndGetFloat64(DCM_PixelSpacing, pixelY, 0);
        result = dset->findAndGetFloat64(DCM_PixelSpacing, pixelX, 1);

        result = dset->findAndGetFloat64(DCM_WindowWidth, winWidth);
        result = dset->findAndGetFloat64(DCM_WindowCenter, winCenter);
        defCenter = winCenter;
        defWidth = winWidth;

        dcmImage = new DicomImage(dset, dset->getOriginalXfer());
        if (dcmImage->getStatus() == EIS_Normal) {
            if (winWidth < 1) {
                dcmImage->setRoiWindow(0, 0, dcmImage->getWidth(), dcmImage->getHeight());
                dcmImage->getWindow(winCenter, winWidth);
                defCenter = winCenter;
                defWidth = winWidth;
            }
        } else {
            delete dcmImage;
            dcmImage = 0;
        }
    }
}

void ImageInstanceData::setRoiWindow(const QRectF &rect)
{
    if (isNormal()) {
        dcmImage->setRoiWindow(rect.left(), rect.top(), rect.width(), rect.height());
        dcmImage->getWindow(winCenter, winWidth);
    }
}

void ImageInstanceData::setFullDynamic()
{
    if (isNormal()) {
        dcmImage->setMinMaxWindow();
        dcmImage->getWindow(winCenter, winWidth);
    }
}

void ImageInstanceData::setPolarity(EP_Polarity polarity)
{
    if (isNormal()) {
        dcmImage->setPolarity(polarity);
    }
}

EP_Polarity ImageInstanceData::getPolarity() const
{
    return isNormal()?dcmImage->getPolarity():EPP_Normal;
}

const DicomImage *ImageInstanceData::getDcmImage() const
{
    return dcmImage;
}

DcmFileFormat *ImageInstanceData::getFileFormat() const
{
    return dcmff;
}

bool ImageInstanceData::saveFileFormat()
{
    DcmDataset *dset;
    DicomImage *image, *tmp;
    if (isNormal()) {
        dset = dcmff->getDataset();
        image = new DicomImage(dcmff, dset->getOriginalXfer());
        tmp = dcmImage;
        dcmImage = image;
        delete tmp;
        dset->findAndGetFloat64(DCM_WindowWidth, winWidth);
        dset->findAndGetFloat64(DCM_WindowCenter, winCenter);
        defCenter = winCenter;
        defWidth = winWidth;
        dcmImage->setWindow(winCenter, winWidth);
        if (!imageFile.isEmpty())
            return EC_Normal == dcmff->saveFile(imageFile.toLocal8Bit().data());
    }
    return false;
}

bool ImageInstanceData::dcm2bmpHelper(DicomImage &dcmImage, QPixmap &pixmap)
{
    int w = dcmImage.getWidth();
    int h = dcmImage.getHeight();
    void *pDIB = 0;
    int size = dcmImage.createWindowsDIB(pDIB, 0, 0, 32, 0, 1);
    if (size == w * h * 4) {
        QImage image((uchar*)pDIB, w, h, QImage::Format_RGB32, &ImageInstanceData::freeBuffer, pDIB);
        pixmap = QPixmap::fromImage(image);
        return !pixmap.isNull();
    }
    return false;
}

bool ImageInstanceData::getPixmap(QPixmap &pixmap)
{
    bool ret = false;

    if (isNormal()) {
        if (winWidth < 1) winWidth = 1;
        dcmImage->setWindow(winCenter, winWidth);
        ret = dcm2bmpHelper(*dcmImage, pixmap);
    }

    return ret;
}

bool ImageInstanceData::getPixmap(const QString &dicomFile, QPixmap &pixmap)
{
    ImageInstanceData image(dicomFile);
    return image.getPixmap(pixmap);
}

DicomImage* ImageInstanceData::createClippedImage(const QRect &rect, int angle, bool hflip, bool vflip, bool inverted)
{
    DicomImage *image = dcmImage;
    if (!image) return image;

    int ret = 1;
    double min, max;
    image->getMinMaxValues(min, max);
    Uint16 pvalue = image->getPhotometricInterpretation()==EPI_Monochrome1?max:min;
    DicomImage *newImage = image->createClippedImage(rect.left(), rect.top(), rect.width(), rect.height(), pvalue);
    if (newImage) {
        if (ret && angle) ret = newImage->rotateImage(angle%360);
        if (ret && hflip) ret = newImage->flipImage(1, 0);
        if (ret && vflip) ret = newImage->flipImage(0, 1);
        if (ret && inverted) ret = newImage->setPolarity(EPP_Reverse);
        if (!ret) {
            delete newImage;
            newImage = 0;
        }
    }
    return newImage;
}

QString ImageInstanceData::getTagKeyValue(const DcmTagKey &key) const
{
    OFString val;
    if (dcmff && dcmff->getDataset())
        dcmff->getDataset()->findAndGetOFString(key, val);
    return QString::fromLocal8Bit(val.c_str());
}

double ImageInstanceData::getPixelValue(long x, long y) const
{
    DicomImage *image = dcmImage;

    if (image) {
        const DiPixel* pixel = image->getInterData();
        if (pixel && (x < (long)image->getWidth()) && (x >= 0)
                && (y < (long)image->getHeight()) && (y >= 0)) {
            EP_Representation r = pixel->getRepresentation();
            switch (r) {
            case EPR_Sint8:
                return *((char*)(pixel->getData()) + (y * image->getWidth() + x));
            case EPR_Uint8:
                return *((uchar*)(pixel->getData()) + (y * image->getWidth() + x));
            case EPR_Sint16:
                return *((short*)(pixel->getData()) + (y * image->getWidth() + x));
            case EPR_Uint16:
                return *((ushort*)(pixel->getData()) + (y * image->getWidth() + x));
            case EPR_Sint32:
                return *((int*)(pixel->getData()) + (y * image->getWidth() + x));
            case EPR_Uint32:
                return *((uint*)(pixel->getData()) + (y * image->getWidth() + x));
            }
        }
    }

    return 0;
}

bool ImageInstanceData::getPixSpacing(double &spacingX, double &spacingY) const
{
    if (isNormal()) {
        spacingX = pixelX;
        spacingY = pixelY;
        return true;
    }
    return false;
}

bool ImageInstanceData::getImageSize(ulong &width, ulong &height) const
{
    if (isNormal()) {
        width = dcmImage->getWidth();
        height = dcmImage->getHeight();
        return true;
    }
    return false;
}

const short *ImageInstanceData::getInternalPtr() const
{
    return isNormal()?(const short*)dcmImage->getInterData()->getData():0;
}

const ushort *ImageInstanceData::getRawData() const
{
    if (isNormal()) {
        const ushort *data = 0;
        OFCondition cond = dcmff->getDataset()->findAndGetUint16Array(DCM_PixelData, data);
        return cond.bad()?0:data;
    }
    return 0;
}
